package com.element.security.resource;

import com.element.security.exception.AuthExceptionEntryPoint;
import com.element.security.resource.config.AuthBearerTokenExtractor;
import com.element.security.resource.config.GlobalOAuth2AuthenticationManager;
import com.element.security.resource.config.PermitAllUrlProperties;
import com.element.security.resource.filter.GlobalSecurityFilter;
import com.element.security.resource.filter.SecurityHandlerInterceptor;
import com.element.security.resource.handler.GoAccessDeniedHandler;
import com.element.security.resource.handler.GoAuthenticationEntryPoint;
import com.element.security.resource.handler.GoAuthenticationFailureHandler;
import com.element.security.resource.service.RedisAuthTokenService;
import com.element.security.server.config.TokenStoreConfig;
import com.element.security.server.global.granter.ParamAuthenticationProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;

import javax.annotation.Resource;
import java.util.List;

/**
 * oauth2资源服务器配置
 *
 * @auther zhangwj
 * @date 2021/3/10 下午8:18
 */
@Configuration
@EnableResourceServer
@ConditionalOnWebApplication
@AutoConfigureAfter(PermitAllUrlProperties.class)
@ConditionalOnClass(AuthBearerTokenExtractor.class)
@Import({PermitAllUrlProperties.class, SecurityHandlerInterceptor.class, TokenStoreConfig.class,
        GlobalSecurityFilter.class, RedisAuthTokenService.class})
public class GlobalResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Value(value = "${security.oauth2.resource.id}")
    private String resourceId;

    @Resource
    private PermitAllUrlProperties permitAllUrlProperties;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        List<String> ignoreUrls = permitAllUrlProperties.getIgnoreUrls();
        String[] urls = new String[ignoreUrls.size()];
        urls = ignoreUrls.toArray(urls);
        http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .httpBasic().authenticationEntryPoint(new GoAuthenticationEntryPoint())
                .and().authorizeRequests().antMatchers(urls).permitAll()
                .antMatchers("/**").authenticated().anyRequest().permitAll()
                .and().formLogin()
                .failureHandler(new GoAuthenticationFailureHandler()).permitAll();
        http.exceptionHandling()
                .accessDeniedHandler(new GoAccessDeniedHandler());

        // 添加参数授权
        ParamAuthenticationProvider paramAuthenticationProvider = new ParamAuthenticationProvider(null);
        http.authenticationProvider(paramAuthenticationProvider);
    }

    @Resource
    private TokenStoreConfig tokenStoreConfig;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        TokenStore tokenStore = tokenStoreConfig.getTokenStore();

        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore);
        resources.resourceId(resourceId);
        resources.tokenServices(defaultTokenServices);
        resources.authenticationEntryPoint(new AuthExceptionEntryPoint());
        resources.tokenExtractor(authBearerTokenExtractor());

        GlobalOAuth2AuthenticationManager oAuth2AuthenticationManager = new GlobalOAuth2AuthenticationManager();
        oAuth2AuthenticationManager.setResourceId(resourceId);
        oAuth2AuthenticationManager.setTokenServices(defaultTokenServices);
        resources.authenticationManager(oAuth2AuthenticationManager);
    }

    @Bean
    @ConditionalOnMissingBean(AuthBearerTokenExtractor.class)
    public AuthBearerTokenExtractor authBearerTokenExtractor() {
        return new AuthBearerTokenExtractor(permitAllUrlProperties);
    }
}

